package top.suven.http.oauth.validator;

import top.suven.base.core.spring.SpringUtil;
import top.suven.http.oauth.facade.OauthServiceFacade;
import top.suven.http.oauth.token.OAuthAuthxRequest;
import top.suven.http.oauth.vo.OAuthTokenBuilder;
import top.suven.http.oauth.vo.OauthClientDetails;
import top.suven.http.oauth.vo.OAuthParameter;
import org.apache.commons.lang.StringUtils;
import org.apache.oltu.oauth2.as.issuer.MD5Generator;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuer;
import org.apache.oltu.oauth2.as.issuer.OAuthIssuerImpl;
import org.apache.oltu.oauth2.as.request.OAuthRequest;
import org.apache.oltu.oauth2.as.request.OAuthTokenRequest;
import org.apache.oltu.oauth2.as.response.OAuthASResponse;
import org.apache.oltu.oauth2.common.OAuth;
import org.apache.oltu.oauth2.common.exception.OAuthSystemException;
import org.apache.oltu.oauth2.common.message.OAuthResponse;
import org.apache.oltu.oauth2.common.message.types.TokenType;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Set;


/**
 * 2018-08-01
 * <p/>
 *  *  客户端	客户详细资料 抽像模块类，用于公共验证和获取客户用户的信息；
 *  * 将通用的行为(方法) 位于此
 *
 * @author suven.wang
 */
public abstract class AbstractOAuthTypeValidator extends OAuthResponseValidator {


    private final String USER_ID = "userId";
    private final String OAUTH_STATE = "state";
    protected OauthServiceFacade oauthService;

    protected OAuthResponse oAuthResponse;
    protected OauthClientDetails clientDetails;

    protected AbstractOAuthTypeValidator(OAuthRequest oauthRequest) {
        super(oauthRequest);
        oauthService = SpringUtil.getBean(OauthServiceFacade.class);
    }

    /**
     * 1.校验客户端clientId是否正确
     * 2.校验类型为 GrantType 或 ResponseType
     * 3.校验实现方法个性属性validateSelf
     */

    public final boolean validate() throws OAuthSystemException {
        //1.校验客户端Id是否正确
        final OauthClientDetails details = clientDetails();
        if (details == null) {
            OAuthResponse oAuthResponse =  invalidClientErrorResponse();
            this.setResponse(oAuthResponse);
            return false;
        }
        return validateSelf();
    }
    /**
     * 验证个性属性参数实现抽像方法
     * @return
     * @throws OAuthSystemException
     */
    protected abstract boolean validateSelf() throws OAuthSystemException;



    protected String getClientId(){
       return oauthRequest.getClientId();
    }

    /**
     * 获取用户信息
     * @return
     */
    protected long getUserId(){
        String user_id =  oauthRequest.getParam(USER_ID);
        if(null != user_id && !"".equals(user_id)){
            return Long.parseLong(user_id);
        }return 0;
    }

    protected String getScope(){
        String scope = oauthRequest.getParam(OAuth.OAUTH_SCOPE);
        return scope;
    }

    protected String getState(){
        return oauthRequest.getParam(OAuth.OAUTH_STATE);
    }
    //签证时间
    protected long getIssuedAt(){
        return System.currentTimeMillis();
    }

    public OAuthResponse getResponse(){
        return this.oAuthResponse;
    }

    protected void setResponse(OAuthResponse oAuthResponse){
        this.oAuthResponse = oAuthResponse;
    }

    protected OauthClientDetails clientDetails() {
        final String clientId = this.getClientId();
        if(null == clientId || "".equals(clientId)){
           return null;
        }
        clientDetails = oauthService.getOauthClientDetailsByClientId(clientId);
        return clientDetails;
    }



    protected boolean checkLogin(long userId,String loginToken)throws OAuthSystemException{
        OAuthParameter user = OAuthParameter.build()
                .setUserId(userId)
                .setRefreshToken(loginToken);
        boolean isLogin = oauthService.checkUserLoginByRefreshToken(user);
        if(!isLogin){
            String redirectUri = oauthRequest.getRedirectURI();
            expiredTokenResponse(loginToken,redirectUri);
        }return true;
    }



    protected OAuthTokenBuilder saveToken(long userId)throws OAuthSystemException{
        //生成token
        OAuthTokenBuilder token =  buildToken(userId);
        boolean isToken = oauthService.saveUserToken(token);
        if(isToken){
            return token;
        }
        return null;
    }

    protected OAuthTokenBuilder refreshToken(String refreshToken)throws OAuthSystemException{
        //生成token
        OAuthIssuer oauthIssuer = new OAuthIssuerImpl(new MD5Generator());
        OAuthTokenBuilder token = buildToken(getUserId(),refreshToken,"");
        OAuthTokenBuilder accessToken = oauthService.refreshToken(token);
        return accessToken;
    }

    protected OAuthTokenBuilder buildToken(long userId) throws OAuthSystemException{
        return buildToken(userId,null,"");
    }
    protected OAuthTokenBuilder buildToken(long userId,String refreshToken,String  redirectUri) throws OAuthSystemException{
        OAuthIssuer oauthIssuer = new OAuthIssuerImpl(new MD5Generator());
        final String accessToken = oauthIssuer.accessToken();
        if(refreshToken == null){
            refreshToken = oauthIssuer.refreshToken();
        }
        OAuthTokenBuilder token = OAuthTokenBuilder
                .build()
                .setUserId(userId)
                .setAccessToken(accessToken)
                .setTokenType(TokenType.BEARER.toString())
                .setRefreshToken(refreshToken)
                .setScope(this.getScope())
                .setIssuedAt(this.getIssuedAt())
                .setRedirectUri(redirectUri);
        return token;
    }
    protected OAuthResponse responseToken( OAuthTokenBuilder accessToken) throws OAuthSystemException{
        String redirectURI = oauthRequest.getRedirectURI();
        OAuthResponse response = responseToken(accessToken,redirectURI,false);
        this.setResponse(response);
        return response;
    }
    /**
     * 创建 AccessToken返回信息对象,生成OAuth响应
     * Create  BasicOAuthToken oAuthToken
     * @param queryOrJson True is QueryMessage, false is JSON message
     * @return OAuthResponse
     * @throws OAuthSystemException
     */
    private OAuthResponse responseToken(OAuthTokenBuilder oAuthToken, String redirectURI, boolean queryOrJson) throws OAuthSystemException {
        final String refreshToken = oAuthToken.getRefreshToken();

        final OAuthASResponse.OAuthTokenResponseBuilder builder = OAuthASResponse
                .tokenResponse(HttpServletResponse.SC_OK)
                .location(redirectURI)
                .setParam(OAuth.OAUTH_USERNAME,oAuthToken.getScope())
                .setParam(USER_ID,String.valueOf(oAuthToken.getUserId()))
                .setAccessToken(oAuthToken.getAccessToken())
                .setExpiresIn(String.valueOf(oAuthToken.getExpiresIn()))
                .setTokenType(oAuthToken.getTokenType());

        if (StringUtils.isNotEmpty(refreshToken)) {
            builder.setRefreshToken(refreshToken);
        }

        return queryOrJson ? builder.buildQueryMessage() : builder.buildJSONMessage();
    }



    /** Authorization接口返回code相关信息对象 **/
    public OAuthResponse responseAuthorizationCode(String authCode) throws OAuthSystemException {
        final long userId = getUserId();
        //Oauth 响应
        HttpServletRequest request = ((OAuthAuthxRequest)oauthRequest).request();
        String redirectURI = oauthRequest.getRedirectURI();
        OAuthASResponse.OAuthAuthorizationResponseBuilder builder =
                OAuthASResponse.authorizationResponse(request, HttpServletResponse.SC_OK);
        //设置授权码
        builder.setCode(authCode).location(redirectURI);
        builder.setParam(OAuth.OAUTH_REDIRECT_URI,redirectURI);
        builder.setParam(USER_ID,String.valueOf(userId));
        OAuthResponse oAuthResponse = builder.buildJSONMessage();
        return oAuthResponse;

    }


    //validate scope
    protected final boolean validateScope()throws OAuthSystemException{

        final Set<String> scopes = oauthRequest.getScopes();
        final String clientDetailsScope = clientDetails.scope();          //read write
        if (scopes.isEmpty()) {
            this.setResponse( invalidScopeResponse());
            return false;
        }
        if(null == clientDetailsScope || "".equals(clientDetailsScope)){
            this.setResponse( invalidScopeResponse());
            return false;
        }
        for (String scope : scopes) {
            if (!clientDetailsScope.contains(scope)) {
                logger.debug("Invalid scope - ClientDetails scopes '{}' exclude '{}'", clientDetailsScope, scope);
                this.setResponse( invalidScopeResponse());
                return false;
            }
        }
        return true;
    }

    //validate state
    protected final boolean validateState(String state)throws OAuthSystemException{
        if (StringUtils.isEmpty(state)) {
            logger.debug("Invalid 'state', it is required, but it is empty");
            this.setResponse( invalidStateResponse());
            return false;
        }
        return true;
    }


    // validate ClientSecret
    protected boolean validateClientSecret() throws OAuthSystemException {
        final String clientSecret = oauthRequest.getClientSecret();
        if (clientSecret == null || !clientSecret.equals(clientDetails.getClientSecret())) {
            logger.debug("Invalid client_secret '{}', client_id = '{}'", clientSecret, clientDetails.getClientId());
            this.setResponse( invalidClientSecretResponse());
            return false;
        }
        return true;
    }

    // validate RedirectUri
    protected boolean validateRedirectUri() throws OAuthSystemException {
        final String redirectUri = oauthRequest.getRedirectURI();
        if (redirectUri == null || !redirectUri.equals(clientDetails.getRedirectUri())) {
            logger.debug("Invalid redirectUri [{}]", redirectUri);
            this.setResponse( invalidRedirectUriResponse());
            return false;
        }
        return true;
    }

    protected boolean validateCodeResponse(String oauthCode)throws OAuthSystemException{
        final String authCode = ((OAuthTokenRequest)oauthRequest).getCode();
        if (oauthCode == null || !authCode.equals(authCode)) {
            logger.debug("Invalid code '{}', client_id = '{}'", authCode, clientDetails.getClientId());
            this.setResponse( invalidCodeResponse(authCode));
            return false;
        }
        return true;
    }




}
